package com.niknik.woody.utils.wunit.junit.rule;

import com.niknik.woody.utils.wunit.junit.runner.WUnitContext;
import com.niknik.woody.utils.wunit.log.WLog;
import com.niknik.woody.utils.wunit.log.WLogger;
import org.junit.rules.ExternalResource;
import org.mockito.Mockito;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;

import static com.niknik.woody.utils.wunit.junit.matcher.IsEx.isEx;
import static org.mockito.Matchers.argThat;

public class LogMonitorRule
    extends ExternalResource {

  private WLogger logMock = Mockito.mock(WLogger.class);
  private List<Runnable> afterCheckers = new LinkedList<Runnable>();
  private WUnitContext context;

  public LogMonitorRule(WUnitContext context) {
    this.context = context;
  }

  @Override
  protected void before()
      throws Throwable {
    super.before();

    context.getMockKeeper().register(logMock);
    WLog.interceptWith(logMock);
    afterCheckers.clear();
  }

  @Override
  protected void after() {
    super.after();

    WLog.removeInterceptor();
    for (Runnable logChecker : afterCheckers) {
      logChecker.run();
    }
  }

  private <T extends Throwable> void expectExThrow(final Class<T> clazz, final String msg) {
    afterCheckers.add(new Runnable() {

      @Override
      public void run() {
        context.getExCollector().checkSucceeds(new Callable<Object>() {

          @Override
          public Object call()
              throws Exception {
            context.getMockKeeper().verify(logMock).exThrow(argThat(isEx(clazz, msg)));
            return true;
          }
        });
      }
    });
  }

  public void expectAssertionThrow(String msg) {
    expectExThrow(AssertionError.class, msg);
  }

  public void expectArgThrow(String msg) {
    expectExThrow(IllegalArgumentException.class, msg);
  }

  public void expectStateThrow(String msg) {
    expectExThrow(IllegalStateException.class, msg);
  }
}